\section{kbuild核心流程}
当用户在linux源代码目录下执行make的时候，构建系统就开始运作了。最开始执行的就是源代码根目录下的Makefile，linux的文档管它叫'top level Makefile'。
简单的说它包括如下共呢
\begin{compactenum}
\item 配置内核并产生.config文件
\item 生成include/linux/version.h文件
\item 根据目标arch的类型建立include/asm软链接
\item 更新所有的依赖
\item 递归的便利所有在init-*, core* drivers-* net-* libs*并build所有目标
\item 将生成的文件链接成vmlinux并存放在源代码根目录下
\item 最后准备用于安装的文件
\end{compactenum}

\subsection{变量初始化}
\subsubsection{LDFLAGS}
它是通用链接选项，所有的ld命令都会是用它
\subsubsection{LDFLAGS\_MODULE}
此选项用于生成可加载模块.ko的ld命令
\subsubsection{LDFLAGS\_vmlinux}
顾名思义，它是用于链接vmlinux的选项
\subsubsection{OBJCOPYFLAGS}
某些链接器不支持binary格式，此时可以通过objcopy命令来实现。例如
\begin{lstlisting}[language=bash]
OBJCOPYFLAGS := -O binary
# arch/i386/boot/Makefile
$(obj)/image: vmlinux FORCE
    $(call if_changed,objcopy)
\end{lstlisting}
\subsubsection{AFLAGS}
汇编程序的通用选项
\subsubsection{CFLAGS}
通用的C语言编译选项
\subsubsection{CFLAGS\_KERNEL}
生成内核的通用编译选项
\subsubsection{CFLAGS\_MODULE}
生成可加载模块的编译选项
\subsection{递归编译所有subdir}
真正的构建开始了，top level Makefile会按照一定次序递归访问subdir下的Makefile。这些子目录通过以下几个变量定义:
head-y、init-y、core-y、libs-y、drivers-y、net-y
\begin{compactenum}
\item head-y: 所有需要首先放到vmlinux的目标
\item libs-y: 所有用来生成lib.a的目录
\end{compactenum}
这些变量的定义分成两个部分，一部分在top level Makefile, 它定义了所有与平台架构无关的目录。另外一部分与平台相关的则定义在arch/\$\{ARCH\}/Makefile当中。
\begin{lstlisting}[language=make]
core-y += arch/i386/kernel/
libs-y += arch/i386/lib
drivers-$(CONFIG_OPROFILE)  += arch/sparc64/oprofile/
\end{lstlisting}

\subsection{kbuild Commands}
kbuild系统中提供了很有常用的宏，链接它们对于阅读Linux Makefile非常有帮助。
\subsubsection{if\_changed}
可能读者会非常疑惑，前面提到，按道理Makefile会自己监控依赖项。如果依赖项变化了，Makefile会自己发现并重新生成直接依赖或者间接依赖次项的内容。那为什么还需要if\_chaneged这种功能呢？由GNU make自己来作者事情不是更好吗。
这是由于Linux的Makefile并没有按照最经典的依赖方式组织代码，这就造成了make无法监控更新，不得已需要有一个if\_changed这样的函数。
if\_changed有一个隐含约束，他检查的内容必须保存在变量\$\{targets\}当中。
\begin{lstlisting}[language=make]
 target: source(s) FORCE
     $(call if_changed,ld/objcopy/gzip)
\end{lstlisting}

Linux支持多种架构同时也支持多种编译器，但不同的编译器很多选项是不一样的，某些不同的编译器支持也不同。kbuild提供了一组函数用于判断当前使用的编译器是否支持指定选项。
这些函数包括cc\-option、cc\-option\-yn、cc\-option\-align、cc\-version等等。
在很多Makefile当中都可以找到使用它们的例子，如arch/i386/Makefile。
\begin{lstlisting}[language=make]
cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
\end{lstlisting}
上面的代码会判断编译器是否支持“-march=pentium-mmx“，如果支持则使用。如果不支持就用“-march=i586”选项代替。

后面我们会尝试实现一个类似的Build系统，但由于我们只用GCC，这些函数我们就不用啦。为了方便读者自己阅读Linux的代码，我们在这里还是要顺便说明一下。


它们的实现在根目录的Makefile当中，基本都是通过shell命令完成的，如cc-option的实现如下。
\begin{lstlisting}[language=make]
cc-option = $(shell if $(CC) $(CFLAGS) $(1) \
               -S -o /dev/null -xc /dev/null \
               > /dev/null 2>&1; then \
               echo ``$(1)''; else echo ``$(2)''; fi ;)

\end{lstlisting}

\section{Linux目录结构}
\dirtree{%
.1 rlinux.
.2 arch.
.3 i386.
.3 mm.
.3 kernel.
.3 mach-default.
.3 boot.
.2 drivers.
.2 fs.
.2 include.
.3 asm.
.3 asm-i386.
.3 linux.
.2 init.
.2 kernel.
.3 timers.
.2 lib.
.2 mm.
.2 scripts.
}
